package im.composer.audio.window;

import im.composer.audio.engine.InOut;
import im.composer.audio.engine.Source;

import org.jaudiolibs.audioservers.AudioConfiguration;
import org.tritonus.share.sampled.FloatSampleBuffer;

public class AudioWindow extends InOut {

	private WindowType type;
	private FloatSampleBuffer storage0, storage1;
	private int windowingParts;

	public AudioWindow(AudioConfiguration context) {
		super(context);
	}

	@Override
	public synchronized void configure(AudioConfiguration context) throws Exception {
		super.configure(context);
		storage0 = new FloatSampleBuffer(context.getOutputChannelCount(), context.getMaxBufferSize(), context.getSampleRate());
		storage1 = new FloatSampleBuffer(context.getOutputChannelCount(), context.getMaxBufferSize(), context.getSampleRate());
	}

	private void refreshStorage() {
		storage1.copyTo(storage0, 0, getBufferSize());
		storage1.makeSilence();
		for (Source s : getSources()) {
			try {
				storage1.mix(s.getBuffer());
			} catch (Exception e) {
			}
		}
	}

	public WindowType getWindowType() {
		return type;
	}

	public void setWindowType(WindowType type) {
		this.type = type;
	}

	public int getWindowingParts() {
		return windowingParts;
	}

	public void setWindowingParts(int windowingParts) {
		if (windowingParts < 1) {
			throw new IllegalArgumentException("windowingParts at least be 1");
		}
		this.windowingParts = windowingParts;
	}

	@Override
	protected void computeBuffer() {
		checkSampleOffset();
		FloatSampleBuffer fsb = prepareWindowingBuffer();
		float[] window = getWindowType().getWindow(getBufferSize());
		windowing(fsb,window);
		fsb.copyTo(buf, 0, getBufferSize());
		incrementSampleOffset();
	}

	private void windowing(FloatSampleBuffer fsb, float[] window) {
		for(int i=0;i<fsb.getChannelCount();i++){
			float[] channel = fsb.getChannel(i);
			for(int j=0;j<window.length;j++){
				channel[j]*=window[j];
			}
		}
	}

	private FloatSampleBuffer prepareWindowingBuffer() {
		FloatSampleBuffer fsb = new FloatSampleBuffer(storage0.getChannelCount(),getBufferSize(),storage0.getSampleRate());
		fsb.mix(storage0, getSampleOffset()+getBufferSize(), 0, -getSampleOffset());
		fsb.mix(storage1, 0, -getSampleOffset(), getBufferSize()-getSampleOffset());
		return fsb;
	}

	private void checkSampleOffset() {
		if (getSampleOffset() + calcCuttingOffset() > getBufferSize()) {
			refreshStorage();
			setSampleOffset(-calcCuttingOffset());
		}
	}

	private int calcCuttingOffset() {
		return bufferSize / windowingParts;
	}

	private void incrementSampleOffset() {
		setSampleOffset(getSampleOffset() + calcCuttingOffset());
	}
}
